//
// Graphics.h
//

#ifndef __GRAPHICS_H__
#define __GRAPHICS_H__


#include "SevenHeader.h"
#include "DebugFrame.h"

class CGraphics : public CDebugFrame
{
public:
	struct Vertex
	{
		float x, y, z;
		float u, v;
		static const DWORD _fvf = D3DFVF_XYZ | D3DFVF_TEX1;

		void set(float _x, float _y, float _z, float _u, float _v)
		{
			x = _x, y = _y, z = _z;
			u = _u, v = _v;
		}
	};
	
	enum RenderAlign
	{
		RenderAlign_LeftTop = 0,
		RenderAlign_Center = 1
	};

public:
	CGraphics(void);
	~CGraphics(void);

	bool init(HWND hWnd, UINT uWidth = 0, UINT uHeight = 0, bool isWindowed = false);
	void release(void);

	bool preRender(void);
	void postRender(void);

	void testRender(void);

	void clearScene(D3DCOLOR Color);
	void setTexture(LPDIRECT3DTEXTURE9 pTexture);

	void setAlign(RenderAlign align);
	void transformWorld(const D3DXMATRIX &matWorld);
	void initializeTextureStage(UINT uStage);
	bool createFont(LPD3DXFONT *ppFont, const wchar_t *pwszFontName, UINT uSize, bool bBold, bool bItalic);
	bool createTextureFromFile(const wchar_t *pwszFileName, LPDIRECT3DTEXTURE9 *ppTexture, bool isNonPowerOfTwo = false);
	bool createTextureFromMemory(void *pData, size_t uSize, LPDIRECT3DTEXTURE9 *ppTexture, bool isNonPowerOfTwo = false);
	bool createVertexBuffer(UINT uSize, LPDIRECT3DVERTEXBUFFER9 *ppVertexBuffer, bool isWriteOnly = true);
	void drawVertexBuffer(LPDIRECT3DVERTEXBUFFER9 pVertexBuffer, UINT uOffset, UINT uCount, D3DPRIMITIVETYPE type = D3DPT_TRIANGLELIST);
	void associateTexture(LPDIRECT3DTEXTURE9 pTexture, UINT uStage);

	void makeBillboard(LPDIRECT3DVERTEXBUFFER9 pVertexBuffer, float halfWidth, float halfHeight);
	void makeBox(LPDIRECT3DVERTEXBUFFER9 pVertexBuffer, float halfWidth, float halfHeight, float halfDepth);

	void resetDevice(void);
	void applyShaderEffect(CShaderEffect *pEffect);

	inline UINT getWidth(void) const { return _uWidth; }
	inline UINT getHeight(void) const { return _uHeight; }
	inline LPDIRECT3D9 getDirect3DInterface(void) const { return _pDirect3D; }
	inline LPDIRECT3DDEVICE9 getDeviceInterface(void) const { return _pDevice; }

	inline void enableAlphaBlend(void) { _pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); }
	inline void disableAlphaBlend(void) { _pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); }
	inline void hideCursor(void) { ShowCursor(FALSE); }
	inline void showCursor(void) { ShowCursor(TRUE); }

	inline bool isDeviceNeedReset(void) { return D3DERR_DEVICENOTRESET == _pDevice->TestCooperativeLevel(); }

private:
	LPDIRECT3D9				_pDirect3D;
	LPDIRECT3DDEVICE9		_pDevice;
	D3DMULTISAMPLE_TYPE		_MsaaLevel;
	D3DPRESENT_PARAMETERS	_PresentParams;

	HWND	_hTarget;
	UINT	_uWidth, _uHeight, _uRefresh;
	DWORD	_dwMsaaQuality;
	bool	_bHardwareAccel;

private:	// Graphics Attributes
	D3DXMATRIX	_matView;
	D3DXMATRIX	_matProjection;
	CShaderEffect *_pEffect;
	LPD3DXFONT	_pFont;

private:
	void _setRenderParameters(void);
	bool _checkPrerequisite(UINT uWidth, UINT uHeight, bool isWindowed);
	void _drawFps(void);
};

#define ReleaseTexture(texture)			safe_release<LPDIRECT3DTEXTURE9>(&texture)
#define ReleaseFont(font)				safe_release<LPD3DXFONT>(&font)
#define ReleaseVertexBuffer(vertexbuf)	safe_release<LPDIRECT3DVERTEXBUFFER9>(&vertexbuf)
#define ReleaseDevice(device)			safe_release<LPDIRECT3DDEVICE9>(&device)
#define ReleaseDirect3D(direct3d)		safe_release<LPDIRECT3D9>(&direct3d)
#define ReleasePixelShader(pshader)		safe_release<LPDIRECT3DPIXELSHADER9>(&pshader)
#define ReleaseVertexShader(vshader)	safe_release<LPDIRECT3DVERTEXSHADER9>(&vshader)

template <typename T>
void safe_release(T *object)
{
	if(*object) (*object)->Release();
	(*object) = NULL;
}

#endif // __GRAPHICS_H__
